#include "GraphicsScene.h"
#include "GraphicsView.h"

#include <QGraphicsItem>
#include <cmath>
#include "LineItem.h"
#include "OperatorsInterface/EventOperator.h"
#include "FITK_Kernel/FITKCore/FITKOperatorRepo.h"
#include "GraphicsItem.h"

namespace Graphics
{
	class ScenePrivater
	{
	public:
        /**
         * @brief     background big background grid pen
         * @author    maguiwa(maguiwa520@163.com)
         * @date      2024-9-24 16:47
         */
		QPen _darkPen{ QPen(QBrush(QColor(230,230,230)),1,Qt::DashDotLine) };
		/**
		 * @brief     background small background grid pen
		 * @author    maguiwa(maguiwa520@163.com)
		 * @date      2024-9-24 16:48
		 */
        QPen _lightPen{ QPen(QBrush(QColor(245,245,245)),1,Qt::DashLine) };
        /**
         * @brief     if show background grid
         * @author    maguiwa(maguiwa520@163.com)
         * @date      2024-9-24 16:48
         */
		bool _showGrid{ true };
        /**
         * @brief     graphics background grid interval
         * @author    maguiwa(maguiwa520@163.com)
         * @date      2024-9-24 16:49
         */
		GraphicsScene::SceneInterval _interval;
        /**
         * @brief     canvas mode
         * @author    maguiwa(maguiwa520@163.com)
         * @date      2024-9-24 16:49
         */
		CanvasMode _canvasMode{ Select_Mode };
        /**
         * @brief     while change scene rect. factor size.
         * @author    maguiwa(maguiwa520@163.com)
         * @date      2024-9-24 16:49
         */
		QSize _sceneSizeFactor{ QSize(1000,1000) };
        /**
         * @brief     graphics scene original size
         * @author    maguiwa(maguiwa520@163.com)
         * @date      2024-9-24 16:50
         */
		QSize _sceneOrignSize{ QSize(2000,2000) };
	};

	GraphicsScene::GraphicsScene(QObject *parent /*= nullptr*/)
		: QGraphicsScene(parent)
		, _privater(new ScenePrivater)
	{
		setSceneRect(0, 0, _privater->_sceneOrignSize.width(), _privater->_sceneOrignSize.height());
		connect(this, SIGNAL(selectionChanged()), this, SLOT(onSelectionChanged()));
	}

	GraphicsScene::GraphicsScene(const QRectF &sceneRect, QObject *parent /*= nullptr*/)
		: QGraphicsScene(sceneRect, parent)
		, _privater(new ScenePrivater)
	{
		setSceneRect(0, 0, _privater->_sceneOrignSize.width(), _privater->_sceneOrignSize.height());
		connect(this, SIGNAL(selectionChanged()), this, SLOT(onSelectionChanged()));
	}

	GraphicsScene::GraphicsScene(qreal x, qreal y, qreal width, qreal height, QObject *parent /*= nullptr*/)
		: QGraphicsScene(x, y, width, height, parent)
		, _privater(new ScenePrivater)
	{
		setSceneRect(0, 0, _privater->_sceneOrignSize.width(), _privater->_sceneOrignSize.height());
		connect(this, SIGNAL(selectionChanged()), this, SLOT(onSelectionChanged()));
	}

	GraphicsScene::~GraphicsScene()
	{
		if (_privater)
		{
			delete _privater;
			_privater = nullptr;
		}
	}

	QPen GraphicsScene::darkPen() const
	{
		return _privater->_darkPen;
	}

	void GraphicsScene::setDarkPen(QPen pen)
	{
		_privater->_darkPen = pen;
		update();
	}

	QPen GraphicsScene::lightPen() const
	{
		return _privater->_lightPen;
	}

	void GraphicsScene::setLightPen(QPen pen)
	{
		_privater->_lightPen = pen;
		update();
	}

	bool GraphicsScene::showGrid() const
	{
		return _privater->_showGrid;
	}

	void GraphicsScene::setShowGrid(bool show)
	{
		_privater->_showGrid = show;
		update();
	}

	Graphics::GraphicsScene::SceneInterval GraphicsScene::sceneInterval() const
	{
		return _privater->_interval;
	}

	void GraphicsScene::setSceneInterval(SceneInterval interval)
	{
		_privater->_interval = interval;
	}

	void GraphicsScene::updateSceneRect()
	{
		int leftOut = 0, rightOut = 0, topOut = 0, bottomOut = 0;
		for (auto item : items())
		{
			if (item->parentItem()){ continue; }

			auto topLeft = item->pos().toPoint() + item->boundingRect().topLeft().toPoint();
			auto bottomRight = item->pos().toPoint() + item->boundingRect().bottomRight().toPoint();

			if (topLeft.x() < 0)
				leftOut = std::max(leftOut, -topLeft.x());
			if (bottomRight.x() > _privater->_sceneOrignSize.width())
				rightOut = std::max(rightOut, bottomRight.x() - _privater->_sceneOrignSize.width());
			if (topLeft.y() < 0)
				topOut = std::max(topOut, -topLeft.y());
			if (bottomRight.y() > _privater->_sceneOrignSize.height())
				bottomOut = std::max(bottomOut, bottomRight.y() - _privater->_sceneOrignSize.height());
		}
		int upNum = 0, downNum = 0, leftNum = 0, rightNum = 0;
		topOut != 0 ? upNum = topOut / _privater->_sceneSizeFactor.height() + 1 : 0;
		bottomOut != 0 ? downNum = bottomOut / _privater->_sceneSizeFactor.height() + 1 : 0;
		leftOut != 0 ? leftNum = leftOut / _privater->_sceneSizeFactor.width() + 1 : 0;
		rightOut != 0 ? rightNum = rightOut / _privater->_sceneSizeFactor.width() + 1 : 0;

		auto rect = QRectF(QPointF(-leftNum * _privater->_sceneSizeFactor.width(),
			-upNum * _privater->_sceneSizeFactor.height()),
			QPointF(rightNum * _privater->_sceneSizeFactor.width() + _privater->_sceneOrignSize.width(),
				bottomOut * _privater->_sceneSizeFactor.height() + _privater->_sceneOrignSize.height()));

		setSceneRect(rect);
	}

	void GraphicsScene::setCanvasMode(CanvasMode mode)
	{
		_privater->_canvasMode = mode;
		for (auto item : items())
			item->setSelected(false);

		State state;
		state.mode = mode;

		if (mode == Line_Mode)
			state.lineState = Line_Initial_State;
		else if (mode == Select_Mode)
			state.selectState = Select_Initial_State;

		for (auto view : views())
		{
			GraphicsView * real = dynamic_cast<GraphicsView*>(view);
			if (real)
			{
				real->setState(state);
				real->update();
			}
		}
	}

	CanvasMode GraphicsScene::canvasMode() const
	{
		return _privater->_canvasMode;
	}

	void GraphicsScene::drawBackground(QPainter *painter, const QRectF &rect)
	{
        if (!_privater->_showGrid)
        {
            return QGraphicsScene::drawBackground(painter, rect);
        }
		Q_UNUSED(rect);
		auto sceneRect = this->sceneRect();
		int width = sceneRect.width();
		int height = sceneRect.height();

		int xNum = width / _privater->_interval.bigInterval;
		int yNum = height / _privater->_interval.bigInterval;
		int subNum = ceil(_privater->_interval.bigInterval / (double)_privater->_interval.smallInterval) - 1;

		painter->setPen(_privater->_darkPen);
		for (int iCnt = 0; iCnt <= xNum; ++iCnt)
		{
			int xPos = sceneRect.x() + iCnt * _privater->_interval.bigInterval;
			painter->drawLine(QPoint(xPos, sceneRect.y()), QPoint(xPos, height));
		}

		for (int iCnt = 0; iCnt <= yNum; ++iCnt)
		{
			int yPos = sceneRect.y() + iCnt * _privater->_interval.bigInterval;
			painter->drawLine(QPoint(sceneRect.x(), yPos), QPoint(width, yPos));
		}

		painter->setPen(_privater->_lightPen);
		for (int iCnt = 0; iCnt < xNum; ++iCnt)
		{
			int xPos = sceneRect.x() + iCnt * _privater->_interval.bigInterval;
			for (int iSub = 0; iSub < subNum; ++iSub)
			{
				int xSubPos = xPos + _privater->_interval.smallInterval *(iSub + 1);
				painter->drawLine(QPoint(xSubPos, sceneRect.y()), QPoint(xSubPos, height));
			}
		}
		for (int iCnt = 0; iCnt <= yNum; ++iCnt)
		{
			int yPos = sceneRect.y() + iCnt * _privater->_interval.bigInterval;
			for (int iSub = 0; iSub < subNum; ++iSub)
			{
				int ySubPos = yPos + _privater->_interval.smallInterval *(iSub + 1);
				painter->drawLine(QPoint(sceneRect.x(), ySubPos), QPoint(width, ySubPos));
			}
		}
		QGraphicsScene::drawBackground(painter, rect);
	}

	void GraphicsScene::updateLineItem(GraphicsItem * item)
	{
		for (auto one : items())
		{
			LineItem * line = dynamic_cast<LineItem *>(one);
			if (!line){ continue; }

			if (line->fromGraphicsItem() == item)
				line->setFromGraphicsItem(item);
			else if (line->toGraphicsItem() == item)
				line->setToGraphicsItem(item);
		}
	}

	QList<GraphicsItem *> GraphicsScene::selectedComponents() const
	{
		QList<GraphicsItem *> results;
		for (auto one : selectedItems())
		{
			GraphicsItem * item = dynamic_cast<GraphicsItem *>(one);
			if (item)
				results.append(item);
		}
		return results;
	}

	void GraphicsScene::onSelectionChanged()
	{
		EventOper::EventOperator * oper = Core::FITKOperatorRepo::getInstance()->getOperatorT<EventOper::EventOperator>("CanvasSelectionChanged");
		if (oper)
			oper->triggered();
	}

}